home *** CD-ROM | disk | FTP | other *** search
/ Internet Info 1994 March / Internet Info CD-ROM (Walnut Creek) (March 1994).iso / networking / info-service / gopher / Unix / GopherTools / glog / glog33u.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-07-20  |  33.6 KB  |  1,479 lines

  1. /*** glog33.c -- analysis tool for Unix gopherd logs ***/
  2.  
  3. /* Define NODETAIL if you don't want detail listings to be kept. Detail 
  4.  * listings can double the pointer memory required and slow things down.
  5.  * If you NEVER want to do detail listings then define it.  On fast machines
  6.  * it really doesn't matter, but on my Amiga I can notice the difference.
  7.  */
  8.  
  9. /* #define NODETAIL */
  10.  
  11. /******************** END CONFIGURATION ***************************/
  12. /* That was really hard wasn't it :) */
  13.  
  14.  
  15. /* Bug Reports/suggestions goto */
  16. #define EMAIL "awick@csugrad.cs.vt.edu"
  17.  
  18. #define    GLOG_VERSION "Gopher Log Analyzer 3.3"
  19.  
  20. /* Version 3.3 7/20/93 jdc@selway.umt.edu
  21.  * fixed up main() routine so that: errors on the command line (or -h as
  22.  * first parameter) cause glog to print the help information and abort.
  23.  * fixed up PrintHelp() routine so help message is more understandable
  24.  * changed FILETYPE character from ' ' to 'I' (What about Image?)
  25.  *
  26.  */
  27. /* Version 3.2 
  28.  * Fixed a small bug with search
  29.  */
  30. /* Version 3.1  7/11/93
  31.  * by: Andy Wick - awick@csugrad.cs.vt.edu
  32.  * Added supported for the "missing" gopher types, time plots, month
  33.  * reports and several other things that I forgot :)
  34.  * Used unprotize from gnu to unprototype the functions for external
  35.  * distrubition.  (My version still has prototypes)
  36.  *
  37.  * Version 3.0 
  38.  * by: Andy Wick - awick@csugrad.cs.vt.edu
  39.  * This version is an almost TOTAL rewrite of glog.  It now reads all
  40.  * the information into memory before it does ANYTHING.  It then goes
  41.  * through the arguments one at a time.  So inorder to effect something
  42.  * you must change it before the report.  ie.  Argument order matters now.
  43.  *
  44.  * Version 2.2
  45.  * by: Chuck Shotton - cshotton@oac.hsc.uth.tmc.edu
  46.  *
  47.  * Version 2.1 
  48.  * by: Michael Mealling - Georgia Institute of Technology
  49.  *       Office of Information Technology
  50.  *       michael.meallingl@oit.gatech.edu
  51.  *       12/29/92
  52.  *
  53.  * Versions 1.0
  54.  * by: Chuck Shotton - U of Texas Health Science Center - Houston,
  55.  *    Office    of Academic Computing
  56.  *    cshotton@oac.hsc.uth.tmc.edu
  57.  *    6/17/92
  58.  */
  59.  
  60.  
  61. #include <stdio.h>
  62. #include <string.h>
  63. /* Some machines don't have a stdlib.  You can remove it if need be
  64.  *  it is here for type checking, usually :) 
  65.  */
  66. #include <stdlib.h>
  67. #ifdef THINK_C
  68. #include <console.h> 
  69. #endif
  70.  
  71.  
  72. /* GENERAL STUFF */
  73. typedef unsigned char byte;
  74.  
  75. /* Error log link list */
  76. typedef    struct enode_list {
  77.     char      *data;
  78.     struct enode_list *next;
  79. } ELIST_REC, *ELIST_PTR;
  80.  
  81. /* GOPHER LINE STUFF */
  82.  
  83. /* These are the different types of data that are currenly reconized*/
  84. #define FILETYPE    'I'
  85. #define BINARYTYPE    'B'
  86. #define SOUNDTYPE    'S'
  87. #define DIRTYPE        'D'
  88. #define MAILDIRTYPE    'M'
  89. #define FTPTYPE        'F'
  90. #define RANGETYPE    'R'
  91. #define SEARCHTYPE    '?'
  92.  
  93. /* One line of the gopher log is stored in here */
  94. typedef struct gopher_line {
  95.    byte     day;
  96.    byte     month;
  97.    byte        hour;
  98.    short     date;
  99.    char     *hostname;
  100.    char     *path;
  101.    char     type;
  102. } GOPHER_REC, *GOPHER_PTR;
  103.  
  104. /* A Linked list of gopher lines */
  105. typedef    struct node_list {
  106.     GOPHER_PTR      data;
  107.         short         hits;
  108.     struct node_list *next;
  109. }  LIST_REC, *LIST_PTR;
  110.  
  111.  
  112. /* Main tree */
  113. typedef    struct node_rec    {
  114.     GOPHER_PTR    data;
  115. #ifndef NODETAIL
  116.     LIST_REC     *llist;
  117. #endif
  118.         short         hits;
  119.     struct node_rec    *left, *right;
  120. } NODE_REC, *NODE_PTR;
  121.  
  122. /* The cruft list is a general list for things that aren't parse-able by
  123.  * ProcessLine().  "cruft" kept for historical reasons.
  124.  */
  125. ELIST_PTR     cruft = NULL;
  126.  
  127. /*
  128.  * The following lists are maintained.
  129.  */
  130. NODE_PTR     hosts = NULL;
  131. NODE_PTR     docs  = NULL;
  132. NODE_PTR     days  = NULL;
  133. NODE_PTR     dates = NULL;
  134. NODE_PTR     types = NULL;
  135. NODE_PTR     times = NULL;
  136.  
  137. long int     TotalConnects = 0;
  138.  
  139. /*
  140.  * Self-Documenting vars, that save memory
  141.  */
  142. char         *ROOTNAME = "Root Connections";
  143. char        *Days[7] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
  144. char        *Months[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
  145.                    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
  146.  
  147. /* The base file name for gnuplot reports */
  148. char BASE[7] = "gopher";
  149. char *base = BASE;
  150.  
  151. /* Plot Output. NOT SUPPORTED YET */
  152. #define REPORTOUT    0
  153. #define GNUOUT         1
  154. #define HISTOUT        2
  155.  
  156. /* Type of plot to do */
  157. char OutPlot=GNUOUT;
  158.  
  159. /* Used to tell the Plot routines that you are starting and stoping */
  160. #define PLOTSTART    (GOPHER_PTR)0
  161. #define PLOTDONE    (GOPHER_PTR)1
  162.  
  163. /* Width of reports */
  164. byte     Width     =     62; /* 79 - WIDTHSUB */
  165. #define WIDTHSUB     17  /* The width of the standard print stuff */
  166.  
  167. /* Information */
  168.    /* Internal */
  169. #define NOINFO        0
  170. #define DETAILINFO    1
  171.    /* Changing these will change the command line options also */
  172.    /* Error log requested, but not a valid SORT TYPE */
  173. #define ERRORINFO    'E'
  174.    /* SORT TYPES */
  175. #define DATAINFO     'D'
  176. #define HOSTINFO    'H'
  177. #define WEEKDAYINFO    'W'
  178. #define MONTHDATEINFO    'M'
  179. #define TYPEINFO    'T'
  180. #define TIMEINFO    'I'
  181.  
  182. /* Start stop dates */
  183. char start_date[13], stop_date[13];
  184.  
  185. /* Start stop months */
  186. int mbegin = 1, mend = 12;
  187.  
  188. /* The only forward decleration needed, since I wrote this the pascal way,
  189.  * the way all programs should be written. :) You don't need all the stupid
  190.  * forward declerations, or prototypes.
  191.  */
  192. void PrintInfo();
  193.  
  194.  
  195. /*******************************/
  196. /* My StrStr, since it is not standard on all unix machines (sun 4.0.3) */
  197. char *mystrstr(s1, s2)
  198.      char *s1;
  199.      char *s2;
  200. {
  201. register int len;
  202.  
  203.    len = strlen(s2);
  204.  
  205.    if (len == 0)
  206.       return s1;
  207.  
  208.    while (*s1 != '\0')
  209.    {
  210.       if (strncmp(s1, s2, len) == 0)
  211.          return s1;
  212.       s1++;
  213.    }
  214.  
  215.    return NULL;
  216. }
  217. /*******************************/
  218. /* Add item to error log */
  219. ELIST_PTR InsertErrorLog(list, data)
  220.      ELIST_PTR list;
  221.      char *data;
  222. {
  223. ELIST_PTR temp, temp2;
  224.  
  225.    if (NULL == (temp = (ELIST_PTR)malloc(sizeof(ELIST_REC))))
  226.    {
  227.       fprintf(stderr, "Not enough memory to add to ErrorLog\n");
  228.       exit(1);
  229.    }
  230.    if (NULL == (temp->data = (char *)malloc(sizeof(char) * (strlen(data) +1))))
  231.    {
  232.       fprintf(stderr, "Not enough memory to add to ErrorLog\n");
  233.       exit(1);
  234.    }
  235.    strcpy(temp->data, data);
  236.    temp->next = NULL;
  237.  
  238.    if (list == NULL)
  239.       return (temp);
  240.  
  241.    for (temp2 = list; temp2->next != NULL ; temp2 = temp2->next);
  242.    temp2->next = temp;
  243.  
  244.    return(list);
  245. }
  246. #ifndef NODETAIL
  247. /*******************************/
  248. LIST_PTR InsertDetail(list, data)
  249.      LIST_PTR list;
  250.      GOPHER_PTR data;
  251. {
  252. LIST_PTR temp;
  253.  
  254.       if (NULL == (temp = (LIST_PTR)malloc(sizeof(LIST_REC))))
  255.       {
  256.          fprintf(stderr, "Not enough memory to add to DetailList\n");
  257.          exit(1);
  258.       }
  259.       temp->data = data;
  260.       temp->next = list;
  261.       temp->hits = 1;
  262.       return(temp);
  263. }
  264. #endif
  265. /*******************************/
  266. /* Insert tree_element into the    appropriate symbol table. Increment the
  267.  * number of hits if that element is already present.
  268.  * Insert list_element into linked list    contained in the node that
  269.  * tree_element    was put    in.
  270.  */
  271.  
  272. NODE_PTR Insert(tree, data, cmp)
  273.      NODE_PTR tree;
  274.      GOPHER_PTR data;
  275.      int (*cmp)();
  276. {
  277. int i;
  278.  
  279.    if (tree == NULL)
  280.    {
  281.       if (NULL == (tree = (NODE_PTR) malloc(sizeof(NODE_REC))))
  282.       {
  283.          fprintf(stderr, "No memory for InsertHost\n");
  284.      exit(1);
  285.       }
  286.       tree->data = data;
  287.       tree->left = tree->right = NULL;
  288. #ifndef NODETAIL
  289.       tree->llist = InsertDetail(NULL, data);
  290. #endif
  291.       tree->hits = 1;
  292.       return(tree);
  293.    }
  294.  
  295.    i=cmp(data, tree->data);
  296.  
  297.    if (i > 0)
  298.       tree->right = Insert(tree->right, data, cmp);
  299.    else if (i<0) 
  300.       tree->left = Insert(tree->left, data, cmp);
  301.    else
  302.    {
  303.       tree->hits += 1;
  304. #ifndef NODETAIL
  305.       tree->llist = InsertDetail(tree->llist, data);
  306. #endif
  307.    }
  308.  
  309.    return(tree);
  310. }
  311.  
  312. /*******************************/
  313. NODE_PTR Find(tree, data, cmp)
  314.      NODE_PTR tree;
  315.      GOPHER_PTR data;
  316.      int (*cmp)();
  317. {
  318. int i;
  319.  
  320.    if (tree == NULL)
  321.    {
  322.       return (NULL);
  323.    }
  324.  
  325.    i=cmp(data, tree->data);
  326.  
  327.    if (i > 0)
  328.       return(Find(tree->right, data, cmp));
  329.    if (i<0) 
  330.       return(Find(tree->left, data, cmp));
  331.  
  332.    return(tree);
  333. }
  334. /*******************************/
  335. /* Get a single field from temp, and return the new spot */
  336. char *getf(temp, field)
  337.      char *temp;
  338.      char *field;
  339. {
  340.    while(*temp == ' ')
  341.       temp++;
  342.  
  343.    *field = '\0';
  344.    if (*temp == '\n')
  345.       return(temp);
  346.  
  347.    while ((*temp != ' ') && (*temp != '\0'))
  348.      *field++ = *temp++;
  349.  
  350.    *field = '\0';
  351.    return(temp);
  352. }
  353.  
  354. /*******************************/
  355. int TypesCmp(a, b)
  356.      GOPHER_PTR a;
  357.      GOPHER_PTR b;
  358. {
  359.    return(a->type - b->type);
  360. }
  361. /*******************************/
  362. int TimesCmp(a, b)
  363.      GOPHER_PTR a;
  364.      GOPHER_PTR b;
  365. {
  366.    return(a->hour - b->hour);
  367. }
  368. /*******************************/
  369. int HostsCmp(a, b)
  370.      GOPHER_PTR a;
  371.      GOPHER_PTR b;
  372. {
  373.    return(strcmp(a->hostname, b->hostname));
  374. }
  375. /*******************************/
  376. int DocsCmp(a, b)
  377.      GOPHER_PTR a;
  378.      GOPHER_PTR b;
  379. {
  380.    return(strcmp(a->path, b->path));
  381. }
  382. /*******************************/
  383. int DaysCmp(a, b)
  384.      GOPHER_PTR a;
  385.      GOPHER_PTR b;
  386. {
  387.    return(a->day - b->day);
  388. }
  389. /*******************************/
  390. int DatesCmp(a, b)
  391.      GOPHER_PTR a;
  392.      GOPHER_PTR b;
  393. {
  394. int i = a->month - b->month;
  395.    if (i == 0)
  396.       return(a->date - b->date);
  397.    else
  398.       return(i); 
  399. }
  400. /*******************************/
  401. byte MonthStr2Num(str)
  402.      char *str;
  403. {
  404. static char lastmonth[4] = "Jan"; /* Who knows if saving the last month */
  405. static int lastmonthnum = 1;      /* really makes it faster */
  406. int i;
  407.  
  408.    if (strcmp(lastmonth, str) == 0)
  409.       return(lastmonthnum);
  410.  
  411.    for(i=0;i<12;i++)
  412.       if (strcmp(Months[i], str) == 0)
  413.       {
  414.          strcpy(lastmonth, Months[i]);
  415.      lastmonthnum = i+1;
  416.      return(lastmonthnum);
  417.       }
  418.    return(13); 
  419. }
  420. /*******************************/
  421. byte DayStr2Num(str)
  422.      char *str;
  423. {
  424. static char lastday[4] = "Sun"; /* Same here.  Is there a better way? */
  425. static int lastdaynum = 1;
  426. int i;
  427.  
  428.    if (strcmp(lastday, str) == 0)
  429.       return(lastdaynum);
  430.  
  431.    for(i=0;i<7;i++)
  432.       if (strcmp(Days[i], str) == 0)
  433.       {
  434.          strcpy(lastday, Days[i]);
  435.      lastdaynum = i+1;
  436.      return(lastdaynum);
  437.       }
  438.    return(8); 
  439. }
  440. /*******************************/
  441. /* This routine adds all the data to all the trees.  Now that I have done
  442.  * it this way, I really hate it.  What was I thinking? :)  It makes no
  443.  * sense to have the dates sort be a tree, since it is always the worst
  444.  * case tree :(.  Oh well maybe in 4.0 someone will fix it 
  445.  */
  446. void ADDDATA(data)
  447.      GOPHER_PTR data;
  448. {
  449.    if ((data->month >= mbegin) && (data->month <= mend))
  450.    {
  451.       hosts = Insert(hosts, data, HostsCmp);
  452.       docs  = Insert(docs, data, DocsCmp);
  453.       dates = Insert(dates, data, DatesCmp); /* Slow */
  454.       types = Insert(types, data, TypesCmp);
  455.       times = Insert(times, data, TimesCmp); 
  456.       days  = Insert(days, data, DaysCmp);
  457.       /* Small hack to have start and stop dates */
  458.       if (start_date[0] == '\0')
  459.       {
  460.          sprintf(start_date, "%s %s %d", Days[data->day-1], Months[data->month-1],
  461.           data->date);
  462.       }
  463.       sprintf(stop_date, "%s %s %d", Days[data->day-1], Months[data->month-1],
  464.           data->date);
  465.       TotalConnects++;
  466.    }
  467. }
  468.  
  469. /*******************************/
  470. /* Process a single line completely.  It checks to make sure it is a 
  471.  * Valid line, if not it inserts it into the cruft
  472.  */
  473. void ProcessLine(line)
  474.      char *line;
  475. {
  476. GOPHER_PTR data;
  477. short len;
  478. char *temp; /* Used to save line, incase it is needed for cruft */
  479. char junk[1025];
  480. char message1[1024];
  481. char message2[1024];
  482.  
  483.    if (NULL == (data = (GOPHER_PTR)malloc(sizeof(GOPHER_REC))))
  484.    {
  485.       fprintf(stderr, "Not enough memory. Sorry\n");
  486.       exit(1);
  487.    }
  488.  
  489.    temp = line;
  490.  
  491.    temp = getf(temp, junk); /* Day */
  492.    if (8 == (data->day = DayStr2Num(junk))) 
  493.    { /* Not a real day of week */
  494.       free(data);
  495.       cruft = InsertErrorLog(cruft, line);
  496.       return;
  497.    }
  498.    temp = getf(temp, junk); /* Month */
  499.    if (13 == (data->month = MonthStr2Num(junk)))
  500.    { /* Not a real month */
  501.       free(data);
  502.       cruft = InsertErrorLog(cruft, line);
  503.       return;
  504.    }
  505.    temp = getf(temp, junk); /* Date */
  506.    data->date = atoi(junk);
  507.    temp = getf(temp, junk); /* Time */
  508.    junk[3] = '\0';
  509.    data->hour = atoi(junk); /* Hour */
  510.    temp = getf(temp, junk); /* Year */
  511.    temp = getf(temp, junk); /* What is this number ? */
  512.    temp = getf(temp ,junk); /* hostname */
  513.    if (junk[0] == ':')
  514.    { /* A colon in the hostfield */
  515.       free(data);
  516.       cruft = InsertErrorLog(cruft, line);
  517.       return;
  518.    } 
  519.  
  520.    if (NULL == (data->hostname = (char *)malloc(sizeof(char) * (strlen(junk)+1))))
  521.    {
  522.       fprintf(stderr, "Not enough memory. Sorry\n");
  523.       exit(1);
  524.    }
  525.    strcpy(data->hostname, junk);
  526.  
  527.    temp = getf(temp, junk); /* : COLON */
  528.    if (junk[0] != ':')
  529.    { /* Now we don't have a colon */
  530.       free(data->hostname);
  531.       free(data);
  532.       cruft = InsertErrorLog(cruft, line);
  533.       return;
  534.    } 
  535.    temp = getf(temp, message1);
  536.    temp = getf(temp, message2);
  537.    while((*temp == ' ') && (*temp != '\0'))
  538.        temp++;
  539.    data->path = (char *)malloc(sizeof(char)*(strlen(temp)+1));
  540.    strcpy(data->path, temp);
  541.    data->path[strlen(temp)] = '\0';
  542.  
  543.  
  544.    if (0 != (len = strlen(data->path)))
  545.    {
  546.       if (data->path[len-1] == '\n')
  547.          data->path[len-1] = '\0';
  548.    }
  549.  
  550. /* This one is for that annoying 0.0.0.* IP address then gets stuck
  551.  * in the log when someone is trying to access something you ain't got
  552.  */
  553.  
  554.    if (strncmp(data->hostname,"0.0.0", 5) == 0)
  555.    { 
  556.       free(data->path);
  557.       free(data->hostname);
  558.       free(data);
  559.       cruft = InsertErrorLog(cruft, line);
  560.       return;
  561.    } 
  562.  
  563.    if (strcmp(message1, "Root") == 0)
  564.    {
  565.       data->type = DIRTYPE;
  566.       free(data->path);
  567.       data->path = ROOTNAME;
  568.       ADDDATA(data);
  569.    }
  570.    else if ((strcmp(message1, "retrieved") == 0) && (strcmp(data->path, "/") == 0))
  571.    {
  572.       data->type = DIRTYPE;
  573.       free(data->path);
  574.       data->path = ROOTNAME;
  575.       ADDDATA(data);
  576.    }
  577.    else if (strcmp(message1, "search") == 0)
  578.    {
  579.       strcpy(junk, message2);
  580.  
  581.       if (strncmp(data->path, "for ", 4) == 0)
  582.       {
  583.          /* We found it at the beginning of data->path */
  584.          temp = data->path;
  585.       }
  586.       else if (NULL == (temp = mystrstr(data->path, " for ")))
  587.       { /* No " for " in the search */
  588.          free(data->path);
  589.          free(data->hostname);
  590.          free(data);
  591.          cruft = InsertErrorLog(cruft, line);
  592.          return;
  593.       }
  594.       *temp = '\0'; /* Remove for stuff */
  595.       strcat(junk, " "); /* There is at least one space here */
  596.       strcat(junk, data->path);
  597.       free(data->path);
  598.       data->path = (char *)malloc(sizeof(char)*(strlen(junk)+1));
  599.       strcpy(data->path, junk);
  600.       data->type = SEARCHTYPE;
  601.  
  602.       ADDDATA(data);
  603.    }
  604.    else if (strncmp(message2, "ftp:", 4) == 0)
  605.    {
  606.       strcpy(junk, data->path); /* Incase there was a space in the path */
  607.       free(data->path); /* Then we have to save off path, since it contains it*/
  608.       data->path = (char *)malloc(sizeof(char)*(strlen(message2)+strlen(junk)-2));
  609.       strcpy(data->path, message2+4);
  610.       if (strlen(junk) > 0)
  611.       {
  612.          strcat(data->path, " ");
  613.          strcat(data->path, junk);
  614.       }
  615.       data->type = FTPTYPE;
  616.  
  617.       ADDDATA(data);
  618.    }
  619.    else if (strcmp(message1, "retrieved") == 0)
  620.    {
  621.       if (data->path[0] == '\0')
  622.       { /* We some how retrieved nothing */
  623.          free(data->path);
  624.          free(data->hostname);
  625.          free(data);
  626.          cruft = InsertErrorLog(cruft, line);
  627.          return;
  628.       } 
  629.  
  630.       if (strcmp(message2, "directory") == 0)
  631.          data->type = DIRTYPE;
  632.       else if (strcmp(message2, "maildir") == 0)
  633.          data->type = MAILDIRTYPE;
  634.       else if (strcmp(message2, "file") == 0)
  635.          data->type = FILETYPE;
  636.       else if (strcmp(message2, "binary") == 0)
  637.          data->type = BINARYTYPE;
  638.       else if (strcmp(message2, "sound") == 0)
  639.          data->type = SOUNDTYPE;
  640.       else if (strcmp(message2, "range") == 0)
  641.          data->type = RANGETYPE;
  642.       else
  643.       {
  644.          free(data->path);
  645.          free(data->hostname);
  646.          free(data);
  647.          cruft = InsertErrorLog(cruft, line);
  648.          return;
  649.       } 
  650.       ADDDATA(data);
  651.  
  652.    }
  653.    else /* wasn't anything we know about, g+ maybe?*/
  654.    {
  655.       free(data->path);
  656.       free(data->hostname);
  657.       free(data);
  658.       cruft = InsertErrorLog(cruft, line);
  659.       return;
  660.    } 
  661.  
  662.    return;
  663. }
  664.  
  665. /*******************************/
  666. void GatherInfo()
  667. {
  668. char line[1025];
  669.  
  670.    start_date[0] = '\0';
  671.  
  672.    while(!feof(stdin))
  673.    {
  674.       fgets(line, 1024, stdin);
  675.       if (feof(stdin))
  676.          break;
  677.       ProcessLine(line);
  678.    }
  679. }
  680.  
  681. /*******************************/
  682.  
  683. /* These vars are only valid right after a call to TreeTo?List.  I could have
  684.  *  done some fancy var passing, but why bother. :) 
  685.  */
  686. LIST_PTR GByNum;
  687. int GByNumMax;
  688. int GByNumMin; /* These two will be used for histograms in the future */
  689.     
  690.  
  691. /*******************************/
  692. void InsertSByNum(data, hits)
  693.      GOPHER_PTR data;
  694.      short int hits;
  695. {
  696. LIST_PTR temp, temp2;
  697.  
  698.    if (NULL == (temp = (LIST_PTR)malloc(sizeof(LIST_REC))))
  699.    {
  700.       fprintf(stderr, "Not enough memory in InsertByNum\n");
  701.       exit(1);
  702.    }
  703.    temp->data = data;
  704.    temp->next = NULL;
  705.    temp->hits = hits;
  706.  
  707. /* Figure out some vars */
  708.    if (hits < GByNumMin)
  709.       GByNumMin = hits;
  710.    if (hits > GByNumMax)
  711.       GByNumMax = hits;
  712.  
  713.    if (GByNum == NULL)
  714.       GByNum = temp;
  715.  
  716.    else if (GByNum->hits < hits)
  717.    {
  718.       temp->next = GByNum;
  719.       GByNum = temp;
  720.    }
  721.    else
  722.    {
  723.       temp2 = GByNum;
  724.       while (temp2->next != NULL)
  725.       {
  726.          if (temp2->next->hits < hits)
  727.      {
  728.         temp->next = temp2->next;
  729.         temp2->next = temp;
  730.         return;
  731.      }
  732.      temp2 = temp2->next;
  733.       }
  734.       temp2->next = temp;
  735.    }
  736. }
  737.  
  738. /*******************************/
  739. void InsertUByNum(data, hits)
  740.      GOPHER_PTR data;
  741.      short int hits;
  742. {
  743. LIST_PTR temp;
  744.  
  745.    if (NULL == (temp = (LIST_PTR)malloc(sizeof(LIST_REC))))
  746.    {
  747.       fprintf(stderr, "Not enough memory in InsertByNum\n");
  748.       exit(1);
  749.    }
  750.    temp->data = data;
  751.    temp->next = NULL;
  752.    temp->hits = hits;
  753.  
  754. /* Figure out some vars */
  755.    if (hits < GByNumMin)
  756.       GByNumMin = hits;
  757.    if (hits > GByNumMax)
  758.       GByNumMax = hits;
  759.  
  760.    if (GByNum == NULL)
  761.       GByNum = temp;
  762.  
  763.    else
  764.    {
  765.       temp->next = GByNum;
  766.       GByNum = temp;
  767.    }
  768. }
  769.  
  770. /*******************************/
  771. /* I did two different routines so it would be faster :).  I know this
  772.  * doesn't follow the logic of the rest of the program, but oh well.
  773.  * Do Inorder so that they remain in order, if two have the same
  774.  * num of hits 
  775.  */
  776. void TreeToSList(tree)
  777.      NODE_PTR tree;
  778. {
  779.    if (tree == NULL)
  780.       return;
  781.    
  782.    TreeToSList(tree->left);
  783.    InsertSByNum(tree->data, tree->hits);
  784.    TreeToSList(tree->right);
  785. }
  786.  
  787. /*******************************/
  788. void TreeToUList(tree)
  789.      NODE_PTR tree;
  790. {
  791. /* I did two different routines so it would be faster :).  I know this
  792.  * doesn't follow the logic of the rest of the program, but oh well.
  793.  * Do reverse inorder, so the insert just basicly sticks it at the
  794.  * beginning.  Someone should rewrite this, maybe later :)
  795.  */
  796.    if (tree == NULL)
  797.       return;
  798.    
  799.    TreeToUList(tree->right);
  800.    InsertUByNum(tree->data, tree->hits);
  801.    TreeToUList(tree->left);
  802. }
  803.  
  804. /*******************************/
  805. NODE_PTR ListToTree(list, cmp)
  806.      LIST_PTR list;
  807.      int (*cmp)();
  808. {
  809. NODE_PTR temptree = NULL;
  810.    for(;list != NULL; list = list->next)
  811.       temptree = Insert(temptree, list->data, cmp);
  812.    return(temptree);
  813. }
  814. /*******************************/
  815. void FreeList(list)
  816.      LIST_PTR list;
  817. {
  818. LIST_PTR temp;
  819.    while (list != NULL)
  820.    {
  821.       temp = list;
  822.       list = list->next;
  823.       free(temp);
  824.    }
  825. }
  826. /*******************************/
  827. void FreeTree(tree)
  828.      NODE_PTR tree;
  829. {
  830.    if (tree == NULL)
  831.       return;
  832.    FreeTree(tree->left);
  833.    FreeTree(tree->right);
  834. #ifndef NODETAIL
  835.    FreeList(tree->llist);
  836. #endif
  837.    free(tree);
  838.    return;
  839. }
  840. /*******************************/
  841. /* Given a string and and len, left justify and fill with fill */
  842. void printl(str, len, fill)
  843.      char *str;
  844.      int len;
  845.      char fill;
  846. {
  847.    while (len > 0)
  848.    {
  849.       if (*str == '\n')
  850.          str++;
  851.       if (*str == '\0')
  852.      putc(fill, stdout);
  853.       else
  854.          putc(*str++, stdout);
  855.       len--;
  856.    }
  857. }
  858. /*******************************/
  859. /* Given a string center justify and fill with fill */
  860. void printc(str, fill)
  861.      char *str;
  862.      char fill;
  863. {
  864. int i, len = strlen(str);
  865. int start = (Width + WIDTHSUB - len)/2;
  866.  
  867.    for(i = 0; i < start; i++)
  868.       putc(fill, stdout);
  869.  
  870.    fputs(str, stdout);
  871.  
  872.    if (fill != ' ')
  873.       for(i = start+len; i < Width + WIDTHSUB ; i++)
  874.          putc(fill, stdout);
  875.    putc('\n', stdout);
  876. }
  877. /*******************************/
  878. void PrintData(data)
  879.      GOPHER_PTR data;
  880. {
  881.    if (data == NULL)
  882.    {
  883.       printf("Data:\n");
  884.    }
  885.    else
  886.    {
  887.       printf("%c ",data->type);
  888.       printl(data->path, Width - 2, ' ');
  889.    }
  890. }
  891. /*******************************/
  892. void PrintTime(data)
  893.      GOPHER_PTR data;
  894. {
  895.    if (data == NULL)
  896.    {
  897.       printf("Times:\n");
  898.    }
  899.    else
  900.    {
  901.       printf("%2d",data->hour);
  902.       printl("", Width - 2, ' ');
  903.    }
  904. }
  905. /*******************************/
  906. void PrintType(data)
  907.      GOPHER_PTR data;
  908. {
  909. char *temp;
  910.    if (data == NULL)
  911.    {
  912.       printf("Types:\n");
  913.    }
  914.    else
  915.    {
  916.       switch(data->type)
  917.       {
  918.      case FILETYPE:
  919.         temp = "File";
  920.         break;
  921.      case SOUNDTYPE:
  922.         temp = "Sound";
  923.         break;
  924.      case BINARYTYPE:
  925.         temp = "Binary File";
  926.         break;
  927.      case DIRTYPE:
  928.         temp = "Directory";
  929.         break;
  930.      case MAILDIRTYPE:
  931.         temp = "Mail Directory";
  932.         break;
  933.      case FTPTYPE:
  934.         temp = "FTP";
  935.         break;
  936.      case RANGETYPE:
  937.         temp = "Range";
  938.         break;
  939.      case SEARCHTYPE:
  940.         temp = "Search";
  941.         break;
  942.          default:
  943.         temp = "Unknown";
  944.         break;
  945.       }
  946.       printl(temp, Width, ' ');
  947.    }
  948. }
  949. /*******************************/
  950. void PrintHost(data)
  951.      GOPHER_PTR data;
  952. {
  953.    if (data == NULL)
  954.    {
  955.       printf("Hosts:\n");
  956.    }
  957.    else
  958.    {
  959.       printl(data->hostname, Width, ' ');
  960.    }
  961.  
  962. }
  963. /*******************************/
  964. void PrintDay(data)
  965.      GOPHER_PTR data;
  966. {
  967.    if (data == NULL)
  968.    {
  969.       printf("Days:\n");
  970.    }
  971.    else
  972.    {
  973.       printl(Days[data->day-1], Width, ' ');
  974.    }
  975. }
  976. /*******************************/
  977. void PrintDate(data)
  978.      GOPHER_PTR data;
  979. {
  980.    if (data == NULL)
  981.    {
  982.       printf("Dates:\n");
  983.    }
  984.    else
  985.    {
  986.       
  987.       printf("%3s %3s %d", Days[data->day-1], Months[data->month-1], data->date);
  988.       if (data->date < 10)
  989.          printl("\0", Width - 9, ' ');
  990.       else
  991.          printl("\0", Width - 10, ' ');
  992.    }
  993. }
  994. /*******************************/
  995. void PlotData(rfp, num, data)
  996.      FILE *rfp;
  997.      int num;
  998.      GOPHER_PTR data;
  999. {
  1000.    if (data == PLOTSTART)
  1001.    {
  1002.       fprintf(stderr, "Plot of Data is not currently supported, since I am not quite sure what it is suppose to do.  Mail me ideas: %s\n", EMAIL);
  1003.  
  1004.    }
  1005.  
  1006. }
  1007. /*******************************/
  1008. void PlotType(rfp, num, data)
  1009.      FILE *rfp;
  1010.      int num;
  1011.      GOPHER_PTR data;
  1012. {
  1013. char *temp = NULL;
  1014.    if (data == PLOTSTART)
  1015.    {
  1016.       fprintf(rfp,"set xtics (");
  1017.    }
  1018.    else if (data == PLOTDONE)
  1019.    {
  1020.       fprintf(rfp,"\"\" %d)\n", num);
  1021.       fprintf(rfp,"set data style linespoints\n");
  1022.       fprintf(rfp,"set tics out\n");
  1023.       fprintf(rfp,"set grid\n");
  1024.       fprintf(rfp,"set title \"Gopher Usage\"\n");
  1025.       fprintf(rfp,"plot \"%s.dat\"\n", base);
  1026.    }
  1027.    else
  1028.    {
  1029.       switch(data->type)
  1030.       {
  1031.      case FILETYPE:
  1032.         temp = "File";
  1033.         break;
  1034.      case SOUNDTYPE:
  1035.         temp = "Sound";
  1036.         break;
  1037.      case BINARYTYPE:
  1038.         temp = "Binary File";
  1039.         break;
  1040.      case DIRTYPE:
  1041.         temp = "Directory";
  1042.         break;
  1043.      case MAILDIRTYPE:
  1044.         temp = "Mail Directory";
  1045.         break;
  1046.      case FTPTYPE:
  1047.         temp = "FTP";
  1048.         break;
  1049.      case RANGETYPE:
  1050.         temp = "Range";
  1051.         break;
  1052.      case SEARCHTYPE:
  1053.         temp = "Search";
  1054.         break;
  1055.          default:
  1056.         temp = "Unknown";
  1057.         break;
  1058.       }
  1059.       fprintf(rfp,"\"%s\" %d,", temp, num);
  1060.    }
  1061.  
  1062. }
  1063. /*******************************/
  1064. void PlotHost(rfp, num, data)
  1065.      FILE *rfp;
  1066.      int num;
  1067.      GOPHER_PTR data;
  1068. {
  1069.    if (data == PLOTSTART)
  1070.    {
  1071.       fprintf(stderr, "Plot of Hosts is not currently supported, since I am not quite sure what it is suppose to do.  Mail me ideas: %s\n", EMAIL);
  1072.  
  1073.    }
  1074. }
  1075. /*******************************/
  1076. void PlotDay(rfp, num, data)
  1077.      FILE *rfp;
  1078.      int num;
  1079.      GOPHER_PTR data;
  1080. {
  1081.    if (data == PLOTSTART)
  1082.    {
  1083.       fprintf(rfp,"set xtics (");
  1084.    }
  1085.    else if (data == PLOTDONE)
  1086.    {
  1087.       fprintf(rfp,"\"\" %d)\n", num);
  1088.       fprintf(rfp,"set data style linespoints\n");
  1089.       fprintf(rfp,"set tics out\n");
  1090.       fprintf(rfp,"set grid\n");
  1091.       fprintf(rfp,"set title \"Gopher Usage\"\n");
  1092.       fprintf(rfp,"plot \"%s.dat\"\n", base);
  1093.    }
  1094.    else
  1095.    {
  1096.       fprintf(rfp,"\"%s\" %d,",Days[data->day-1], num);
  1097.    }
  1098. }
  1099. /*******************************/
  1100. void PlotTime(rfp, num, data)
  1101.      FILE *rfp;
  1102.      int num;
  1103.      GOPHER_PTR data;
  1104. {
  1105.    if (data == PLOTSTART)
  1106.    {
  1107.       fprintf(rfp,"set xtics (");
  1108.    }
  1109.    else if (data == PLOTDONE)
  1110.    {
  1111.       fprintf(rfp,"\"\" %d)\n", num);
  1112.       fprintf(rfp,"set data style linespoints\n");
  1113.       fprintf(rfp,"set tics out\n");
  1114.       fprintf(rfp,"set grid\n");
  1115.       fprintf(rfp,"set title \"Gopher Usage\"\n");
  1116.       fprintf(rfp,"plot \"%s.dat\"\n", base);
  1117.    }
  1118.    else
  1119.    {
  1120.       fprintf(rfp,"\"%2d\" %d,",data->hour, num);
  1121.    }
  1122. }
  1123. /*******************************/
  1124. void PlotDate(rfp, num, data)
  1125.      FILE *rfp;
  1126.      int num;
  1127.      GOPHER_PTR data;
  1128. {
  1129.    if (data == PLOTSTART)
  1130.    {
  1131.       fprintf(rfp,"set xtics (");
  1132.    }
  1133.    else if (data == PLOTDONE)
  1134.    {
  1135.       fprintf(rfp,"\"\" %d)\n", num);
  1136.       fprintf(rfp,"set data style linespoints\n");
  1137.       fprintf(rfp,"set tics out\n");
  1138.       fprintf(rfp,"set grid\n");
  1139.       fprintf(rfp,"set title \"Gopher Usage\"\n");
  1140.       fprintf(rfp,"plot \"%s.dat\"\n", base);
  1141.    }
  1142.    else
  1143.    {
  1144.       if ((data->date == 1) || (data->date == 15) || (num == 1))
  1145.          fprintf(rfp,"\"%s/%d\" %d,",Months[data->month-1], data->date, num);
  1146.    }
  1147. }
  1148. #ifndef NODETAIL
  1149. /*******************************/
  1150. void DoDetail(tree, DetailType)
  1151.      NODE_PTR tree;
  1152.      byte DetailType;
  1153. {
  1154. NODE_PTR newtree;
  1155.    switch(DetailType)
  1156.    {
  1157.    case DATAINFO:
  1158.       newtree = ListToTree(tree->llist, DocsCmp);
  1159.       PrintInfo(newtree, PrintData, DocsCmp, DETAILINFO);
  1160.       break;
  1161.    case HOSTINFO:
  1162.       newtree = ListToTree(tree->llist, HostsCmp);
  1163.       PrintInfo(newtree, PrintHost, HostsCmp, DETAILINFO);
  1164.       break;
  1165.    case WEEKDAYINFO:
  1166.       newtree = ListToTree(tree->llist, DaysCmp);
  1167.       PrintInfo(newtree, PrintDay, DaysCmp, DETAILINFO);
  1168.       break;
  1169.    case MONTHDATEINFO:
  1170.       newtree = ListToTree(tree->llist, DatesCmp);
  1171.       PrintInfo(newtree, PrintDate, DatesCmp, DETAILINFO);
  1172.       break;
  1173.    case TYPEINFO:
  1174.       newtree = ListToTree(tree->llist, TypesCmp);
  1175.       PrintInfo(newtree, PrintType, TypesCmp, DETAILINFO);
  1176.       break;
  1177.    case TIMEINFO:
  1178.       newtree = ListToTree(tree->llist, TimesCmp);
  1179.       PrintInfo(newtree, PrintTime, TimesCmp, DETAILINFO);
  1180.       break;
  1181.    default:
  1182.       newtree = NULL;
  1183.       break;
  1184.    }
  1185.    FreeTree(newtree);
  1186. }
  1187. #endif
  1188. /*******************************/
  1189. void PrintInfo(tree, print, cmp, DetailType)
  1190.      NODE_PTR tree;
  1191.      void (*print)();
  1192.      int (*cmp)();
  1193.      byte DetailType;
  1194. {
  1195. LIST_PTR temp;
  1196. LIST_PTR ByNum;
  1197.  
  1198.    GByNum = NULL; /* Init the vars for the TreeToList function */
  1199.    TreeToSList(tree);
  1200.  
  1201.    if (DetailType != DETAILINFO)
  1202.    { /* We are not printing Detail info now, so do headers */
  1203.       print(NULL);
  1204.       printc("", '=');
  1205.    }
  1206.  
  1207.    ByNum = GByNum; /* Save off and clear the globals vars, since this */
  1208.    GByNum = NULL;
  1209.  
  1210.    temp = ByNum;
  1211.    while (temp != NULL)
  1212.    {
  1213. #ifndef NODETAIL
  1214.       if (DetailType == DETAILINFO)
  1215.          printf("   ");
  1216. #endif
  1217.  
  1218.       print(temp->data);
  1219.       printf(" %4d (%2.2f%%)\n", temp->hits, (float)temp->hits*100.0/TotalConnects);
  1220. #ifndef NODETAIL
  1221.       if ((DetailType != NOINFO) && (DetailType != DETAILINFO))
  1222.          DoDetail(Find(tree, temp->data, cmp), DetailType);
  1223.      /* Don't generate Detail for NOINFO or if we are already doing detail */
  1224. #endif
  1225.       temp = temp->next;
  1226.    }
  1227.    printf("\n");
  1228.    FreeList(ByNum);
  1229. }
  1230. /*******************************/
  1231. void PlotInfo(tree, plot)
  1232.      NODE_PTR tree;
  1233.      void (*plot)();
  1234. {
  1235. LIST_PTR temp;
  1236. FILE *rfp, *dfp;
  1237. char *fn;
  1238. int points = 1;
  1239.  
  1240.    if (OutPlot == GNUOUT)
  1241.    {
  1242.       fn = (char *)malloc(strlen(base) + 5);
  1243.       sprintf(fn,"%s.run", base);
  1244.       if (NULL == (rfp = fopen(fn, "w")))
  1245.       {
  1246.          fprintf(stderr, "Could not open file \"%s\" for plot run\n", fn);
  1247.          free(fn);
  1248.      return;
  1249.       }
  1250.       sprintf(fn,"%s.dat", base);
  1251.       if (NULL == (dfp = fopen(fn, "w")))
  1252.       {
  1253.          fprintf(stderr, "Could not open file \"%s\" for plot data\n", fn);
  1254.          free(fn);
  1255.      return;
  1256.       }
  1257.       free(fn);
  1258.    }
  1259.    else
  1260.    {
  1261.       rfp = stdout;
  1262.       dfp = stdout;
  1263.    }
  1264.  
  1265.    plot(rfp, 0, PLOTSTART);
  1266.    GByNum = NULL; /* Init the vars for the TreeToList function */
  1267.    GByNumMax = 0;
  1268.    GByNumMin = 36000;
  1269.    TreeToUList(tree);
  1270.  
  1271.    temp = GByNum;
  1272.    while (temp != NULL)
  1273.    {
  1274.       plot(rfp, points, temp->data);
  1275.       fprintf(dfp, "%d %d\n", points++, temp->hits);
  1276.       temp = temp->next;
  1277.    }
  1278.    plot(rfp, points, PLOTDONE);
  1279.    printf("\n");
  1280.    FreeList(GByNum);
  1281. }
  1282. /*******************************/
  1283. void PrintErrorInfo()
  1284. {
  1285. ELIST_PTR temp = cruft;
  1286.  
  1287.    printf("Exception/Problem Report\n");
  1288.    printf("NOTE: THESE ENTRIES MAY DENOTE A SERVER PROBLEM. THEY SHOULD BE LOOKED OVER!\n");
  1289.    printc("", '=');
  1290.    while (temp != NULL)
  1291.    {
  1292.       printf(temp->data);
  1293.       temp = temp->next;
  1294.    }
  1295.    printf("\n");
  1296. }
  1297.  
  1298. /*******************************/
  1299. void PrintHelp()
  1300. {
  1301. #ifndef NODETAIL
  1302.    fprintf(stderr,"Usage: glog -%c [-<SORTTYPE>[<SORTTYPE>]] [OPTIONS] [<logfile]\n", ERRORINFO);
  1303. #else
  1304.    fprintf(stderr,"Usage: glog -%c [-<SORTTYPE>] [OPTIONS] [<logfile]", ERRORINFO);
  1305. #endif
  1306.    
  1307.    fprintf(stderr, "  glog -h prints this help information\n");
  1308.    fprintf(stderr, "  glog -%c displays an ERROR LOG\n",ERRORINFO);
  1309.    fprintf(stderr, "  stdin is expected to be your gopher logfile\n\n");
  1310.    fprintf(stderr, "SORTTYPE is one of the following\n");
  1311.    fprintf(stderr, "   %c = Host Names       %c = Day of Week \n",
  1312.       HOSTINFO, WEEKDAYINFO);
  1313.    fprintf(stderr, "   %c = Document Names   %c = Month/Day\n",
  1314.     DATAINFO, MONTHDATEINFO);
  1315.    fprintf(stderr, "   %c = Type             %c = Time\n\n", TYPEINFO, TIMEINFO);
  1316.    fprintf(stderr, "OPTIONS is one of the following\n");
  1317.    fprintf(stderr, "   [-b <beginning month #>] [-e <ending month #>]\n");
  1318.    fprintf(stderr, "   [-w <width of report>] [-p <SORTTYPE>] PLOT <by SORTTYPE>\n");
  1319.    fprintf(stderr, "   [-f <base filename for plot>] [-o <plot outputtype>] (currently unsupported)\n\n");
  1320.    fprintf(stderr, "WARNING: -b and -e are applied to the whole command,\n"); 
  1321.    fprintf(stderr, "   any other arguments are applied left to right\n\n");
  1322. }
  1323.  
  1324. /*******************************/
  1325. int main(argc, argv)
  1326.      int argc;
  1327.      char **argv;
  1328. {
  1329. int i;
  1330. char center[80];
  1331.  
  1332. #ifdef THINK_C
  1333.     argc = ccommand(&argv); 
  1334. #endif   
  1335.    printf("\n");
  1336.    printc(GLOG_VERSION,' ');
  1337.    fflush(stdout);
  1338.  
  1339.    if  (1 == argc || argv[1][1] =='h')
  1340.    {
  1341.       PrintHelp(); /* Clueless */
  1342.       exit(-1);
  1343.    }
  1344.  
  1345.    i = 1;
  1346. /* We must go through the arguments twice.  Once for the the arguments that
  1347.  * can only be set once.  I coult add argument checking here, but why bother
  1348.  */
  1349.    while (i<argc)
  1350.    {
  1351.       if (argv[i][0] == '-')
  1352.       switch (argv[i][1])
  1353.       {
  1354.  
  1355.       case 'b':
  1356.          if (i<argc-1)
  1357.         mbegin = atoi(argv[++i]);
  1358.      else
  1359.      {
  1360.         fprintf(stderr, "\nexpected beginning month argument for -%c\n", argv[i-1][1]);
  1361.         PrintHelp();
  1362.      }
  1363.          break;
  1364.       case 'e':
  1365.          if (i<argc-1)
  1366.         mend = atoi(argv[++i]);
  1367.      else
  1368.      {
  1369.         fprintf(stderr, "\nexpected ending month argument for -%c\n", argv[i-1][1]);
  1370.         PrintHelp();
  1371.      }
  1372.          break;
  1373.       }
  1374.       i++;
  1375.    }
  1376.    GatherInfo();
  1377.    sprintf(center, "%s to %s", start_date, stop_date);
  1378.    printc(center, ' ');
  1379.    sprintf(center, "%d Connections", TotalConnects);
  1380.    printc(center, ' ');
  1381.    printf("\n\n");
  1382.    fflush(stdout);
  1383.    fflush(stderr);
  1384.  
  1385.    i = 1;
  1386.    /* Now go through them again and actually do them, from left to right */
  1387.    while (i<argc)
  1388.    {
  1389.       switch (argv[i][1])
  1390.       {
  1391.       case ERRORINFO:
  1392.      PrintErrorInfo();
  1393.      break;
  1394.       case DATAINFO:
  1395.      PrintInfo(docs, PrintData, DocsCmp, argv[i][2]);
  1396.      break;
  1397.       case TYPEINFO:
  1398.      PrintInfo(types, PrintType, TypesCmp, argv[i][2]);
  1399.      break;
  1400.       case TIMEINFO:
  1401.      PrintInfo(times, PrintTime, TimesCmp, argv[i][2]);
  1402.      break;
  1403.       case WEEKDAYINFO:
  1404.          PrintInfo(days, PrintDay, DaysCmp, argv[i][2]);
  1405.      break;
  1406.       case MONTHDATEINFO:
  1407.      PrintInfo(dates, PrintDate, DatesCmp, argv[i][2]);
  1408.      break;
  1409.       case HOSTINFO:
  1410.      PrintInfo(hosts, PrintHost, HostsCmp, argv[i][2]);
  1411.      break;
  1412.       case 'p': /*custom plots*/
  1413.          switch (argv[i][2])
  1414.      {
  1415.          case DATAINFO:
  1416.             PlotInfo(docs, PlotData);
  1417.         break;
  1418.          case TYPEINFO:
  1419.         PlotInfo(types, PlotType);
  1420.         break;
  1421.          case TIMEINFO:
  1422.         PlotInfo(times, PlotTime);
  1423.         break;
  1424.          case WEEKDAYINFO:
  1425.         PlotInfo(days, PlotDay);
  1426.         break;
  1427.          case MONTHDATEINFO:
  1428.         PlotInfo(dates, PlotDate);
  1429.         break;
  1430.          case HOSTINFO:
  1431.         PlotInfo(hosts, PlotHost);
  1432.         break;
  1433.      }
  1434.       break;
  1435.       case 'w':
  1436.          if (i<argc-1)
  1437.         Width = atoi(argv[++i]) - WIDTHSUB;
  1438.      else
  1439.      {
  1440.         fprintf(stderr, "expected width argument for -%c\n", argv[i-1][1]);
  1441.         PrintHelp();
  1442.      }
  1443.      break;
  1444.       case 'o':
  1445.          if (i<argc-1)
  1446.         OutPlot = atoi(argv[++i]);
  1447.      else
  1448.      {
  1449.         fprintf(stderr, "expected output type argument for -%c\n", argv[i-1][1]);
  1450.         PrintHelp();
  1451.      }
  1452.      break;
  1453.       case 'f':
  1454.          if (i<argc-1)
  1455.         base = argv[++i];
  1456.      else
  1457.      {
  1458.         fprintf(stderr, "expected filename argument for -%c\n", argv[i-1][1]);
  1459.         PrintHelp();
  1460.      }
  1461.      break;
  1462.       case 'b':
  1463.       case 'e':
  1464.          i++;   /* Skip over their arguments */
  1465.          break; /* These are before GatherInfo arguments */
  1466.       case '?':
  1467.       default:
  1468.          fprintf(stderr, "Unknown option \"%c\" .  -h for help\n", argv[i][1]);
  1469.      break;
  1470.       } /*switch*/
  1471.     
  1472.       i++; /* next arg...*/
  1473.         
  1474.    } /*while*/
  1475.   
  1476.    exit(0);
  1477. }
  1478. /*******************************/
  1479.